/*
 * scalar.h
 *
 *  Created on: Sep 17, 2008
 *      Author: lindenb
 */
#include <assert.h>
#include <string.h>
#include "lang/morestdlib.h"
#include "util/debug.h"
#include "lexyacc/ynode.h"

#define EVAL(node) (node->eval(node,context))
#define PRINT(node) ((node)->print((node),out))

static void YNodePrint(const struct ynode_t* node,FILE* out)
	{
	if(node==NULL)
		{
		fputs("null",out);
		return;
		}
	fputs("<undefined-node>",out);
	}

static void YNodeDeleteSimpleNode(YNodePtr ptr)
	{
	if(ptr==NULL) return;
	ptr->print(ptr,stderr);

	YNodeDelete(ptr->first);
	YNodeDelete(ptr->second);
	ScalarDelete(ptr->constant);
	Free(ptr);
	}

void YNodeDelete(YNodePtr ptr)
	{
	if(ptr==NULL) return;

	if(ptr->free==NULL)
		{
		ptr->print(ptr,stderr);
		}

	assert(ptr->free!=NULL);
	ptr->free(ptr);
	}

YNodePtr YNodeInit(YNodePtr ptr)
	{
	if(ptr==NULL) return NULL;
	memset(ptr,sizeof(YNode),0);
	ptr->free = YNodeDeleteSimpleNode;
	ptr->print = YNodePrint;
	return ptr;
	}

static YNodePtr YNodeNew(

		)
	{
	YNodePtr ptr= (YNodePtr)Calloc(1,sizeof(YNode));
	if(ptr!=NULL)
		{
		YNodeInit(ptr);
		}
	return ptr;
	}

static void printAnd(const struct ynode_t* node,FILE* out)
	{
	if(node==null)
		{
		fputs("NULL",out);
		}
	else
		{
		fputs("AND(",out);
		PRINT(node->first);fputc(',',out);
		PRINT(node->second);
		fputc(')',out);
		}
	}

static ScalarPtr evalAnd(const struct ynode_t* self,YContextPtr context)
	{
	if(self==NULL || self->first==NULL || self->second==NULL) return NULL;
	ScalarPtr left = EVAL(self->first);
	ScalarPtr right = EVAL(self->second);

	scalar_int_t i = left->type==SCALAR_IS_INTEGER?left->value.integer!=0:0;
	scalar_int_t j = right->type==SCALAR_IS_INTEGER?right->value.integer!=0:0;

	ScalarDelete(left);
	ScalarDelete(right);

	return ScalarNewInt(i==1 && j==1?1:0);
	}

 YNodePtr yNodeMakeAND(YNodePtr a, YNodePtr b)
	 {
	 YNodePtr ptr= YNodeNew();
	 if(ptr==NULL) return NULL;
	 ptr->first =a,
	 ptr->second = b;
	 ptr->eval =evalAnd;
	 ptr->print =printAnd;
	 return ptr;
	 }


 static void printOr(const struct ynode_t* node,FILE* out)
 	{
 	if(node==null)
 		{
 		fputs("NULL",out);
 		}
 	else
 		{
 		fputs("OR(",out);
 		PRINT(node->first);fputc(',',out);
 		PRINT(node->second);
 		fputc(')',out);
 		}
 	}

 static ScalarPtr evalOr(const struct ynode_t* self,YContextPtr context)
 	{
 	if(self==NULL || self->first==NULL || self->second==NULL) return NULL;
 	ScalarPtr left = EVAL(self->first);
 	scalar_int_t i = left->type==SCALAR_IS_INTEGER?left->value.integer!=0:0;
 	//DEBUG2("i=%d",i);
 	ScalarDelete(left);

 	if(i==1)
		{
 		return ScalarNewInt(1);
		}
 	else
		{
 		ScalarPtr right =EVAL(self->second);
 		i  = right->type==SCALAR_IS_INTEGER?right->value.integer!=0:0;
 		ScalarDelete(right);
 		return ScalarNewInt(i!=0?1:0);
		}
 	}

 YNodePtr yNodeMakeOR(YNodePtr a, YNodePtr b)
	 {
	 YNodePtr ptr= YNodeNew();
	 if(ptr==NULL)  return NULL;
	 assert(ptr->free!=NULL);
	 ptr->first =a,
	 ptr->second = b;
	 ptr->eval =evalOr;
	 ptr->print= printOr;
	 return ptr;
	 }

 static void printNot(const struct ynode_t* node,FILE* out)
 	{
 	if(node==null)
 		{
 		fputs("NULL",out);
 		}
 	else
 		{
 		fputs("NOT(",out);
 		PRINT(node->first);
 		fputc(')',out);
 		}
 	}

 static ScalarPtr evalNot(const struct ynode_t* self,YContextPtr context)
 	{
 	if(self==NULL || self->first==NULL || self->second==NULL) return NULL;
 	ScalarPtr left = EVAL(self->first);
 	scalar_int_t i = left->type==SCALAR_IS_INTEGER?left->value.integer!=0:0;
 	ScalarDelete(left);

 	if(i==1)
		{
 		return ScalarNewInt(0);
		}
 	else
		{
 		return ScalarNewInt(1);
		}
 	}

 YNodePtr yNodeMakeNOT(YNodePtr a)
	 {
	 YNodePtr ptr= YNodeNew();
	 if(ptr==NULL)  return NULL;
	 ptr->first =a,
	 ptr->eval =evalNot;
	 ptr->print= printNot;
	 return ptr;
	 }


 static void printCompare(const struct ynode_t* node,FILE* out)
 	{
 	if(node==null)
 		{
 		fputs("NULL",out);
 		}
 	else
 		{
 		switch(node->comparator)
			{
			case Y_NODE_EQ: fputs("eq",out);break;
			case Y_NODE_NE: fputs("ne",out);break;
			case Y_NODE_LT: fputs("lt",out);break;
			case Y_NODE_GT: fputs("gt",out);break;
			case Y_NODE_LE: fputs("le",out);break;
			case Y_NODE_GE: fputs("ge",out);break;
			default:assert(0); break;
			}
 		fputc('(',out);
 		PRINT(node->first);fputc(',',out);
 		PRINT(node->second);fputc(')',out);
 		}
 	}

 static ScalarPtr yCompareNode(const struct ynode_t* self,YContextPtr context)
	 {
	 int i;
	if(self==NULL || self->first==NULL || self->second==NULL) return NULL;
	ScalarPtr left = EVAL(self->first);
	ScalarPtr right = EVAL(self->second);

	 i = ScalarCompare(left,right);

	ScalarDelete(left);

	ScalarDelete(right);

	switch(self->comparator)
		{
		case Y_NODE_EQ:
			{
			return ScalarNewInt(i==0?1:0);
			}
		case Y_NODE_NE:
			{
			return ScalarNewInt(i!=0?1:0);
			}
		case Y_NODE_LT:
			{
			return ScalarNewInt(i<0?1:0);
			}
		case Y_NODE_GT:
			{
			return ScalarNewInt(i>0?1:0);
			}
		case Y_NODE_LE:
			{
			return ScalarNewInt(i<=0?1:0);
			}
		case Y_NODE_GE:
			{
			return ScalarNewInt(i>=0?1:0);
			}
		default:assert(0); break;
		}


	 }

static void printConstant(const struct ynode_t* node,FILE* out)
	{
	if(node==null)
	 		{
	 		fputs("NULL",out);
	 		}
	 	else
	 		{
	 		ScalarPrint(node->constant,out);
	 		}
	}

YNodePtr yNodeMakeCompare(YNodePtr a, enum YNode_Comparator compare,YNodePtr b)
	 {
	 YNodePtr ptr= YNodeNew();
	 if(ptr==NULL) return NULL;
	 ptr->first =a,
	 ptr->second = b;
	 ptr->comparator=compare;
	 ptr->eval =yCompareNode;
	 ptr->print=printCompare;
	 return ptr;
	 }

static ScalarPtr yConstant(const struct ynode_t* self,YContextPtr context)
	{
	return ScalarClone(self->constant);
	}

YNodePtr yNodeMakeConstant(ScalarPtr scalar)
	{
	YNodePtr ptr= YNodeNew();
	if(ptr==NULL) return NULL;
	ptr->constant=scalar;
	ptr->eval= yConstant;
	ptr->print=printConstant;
	return ptr;
	}
